package com.yifeng.repo.controller.statemachine.fsm.audit;

import com.alibaba.cola.statemachine.Action;
import com.alibaba.cola.statemachine.Condition;
import com.alibaba.cola.statemachine.StateMachine;
import com.alibaba.cola.statemachine.StateMachineFactory;
import com.alibaba.cola.statemachine.builder.StateMachineBuilder;
import com.alibaba.cola.statemachine.builder.StateMachineBuilderFactory;
import com.yifeng.repo.base.utils.common.BaseUtil;
import com.yifeng.repo.controller.statemachine.constant.StatemachineEnum;
import com.yifeng.repo.controller.statemachine.fsm.audit.biz.AuditContext;
import com.yifeng.repo.controller.statemachine.fsm.audit.biz.AuditEventEnum;
import com.yifeng.repo.controller.statemachine.fsm.audit.biz.AuditStateEnum;
import com.yifeng.repo.controller.statemachine.fsm.audit.worker.action.AuditDoneAction;
import com.yifeng.repo.controller.statemachine.fsm.audit.worker.action.AuditPassAction;
import com.yifeng.repo.controller.statemachine.fsm.audit.worker.action.AuditRejectAction;
import com.yifeng.repo.controller.statemachine.fsm.audit.worker.condition.AuditDoneCondition;
import com.yifeng.repo.controller.statemachine.fsm.audit.worker.condition.AuditPassCondition;
import com.yifeng.repo.controller.statemachine.fsm.audit.worker.condition.AuditRejectCondition;
import lombok.extern.slf4j.Slf4j;

/**
 * Created by daibing on 2024/9/20.
 */
@Slf4j
public class AuditStatemachine {
    private final Action<AuditStateEnum, AuditEventEnum, AuditContext> auditPassAction;
    private final Action<AuditStateEnum, AuditEventEnum, AuditContext> auditRejectAction;
    private final Action<AuditStateEnum, AuditEventEnum, AuditContext> auditDoneAction;
    private final Condition<AuditContext> auditPassCondition;
    private final Condition<AuditContext> auditRejectCondition;
    private final Condition<AuditContext> auditDoneCondition;

    public AuditStatemachine(Action<AuditStateEnum, AuditEventEnum, AuditContext> auditPassAction,
                             Action<AuditStateEnum, AuditEventEnum, AuditContext> auditRejectAction,
                             Action<AuditStateEnum, AuditEventEnum, AuditContext> auditDoneAction,
                             Condition<AuditContext> auditPassCondition,
                             Condition<AuditContext> auditRejectCondition,
                             Condition<AuditContext> auditDoneCondition) {
        this.auditPassAction = auditPassAction != null ? auditPassAction : new AuditPassAction();
        this.auditRejectAction = auditRejectAction != null ? auditRejectAction : new AuditRejectAction();
        this.auditDoneAction = auditDoneAction != null ? auditDoneAction : new AuditDoneAction();
        this.auditPassCondition = auditPassCondition != null ? auditPassCondition : new AuditPassCondition();
        this.auditRejectCondition = auditRejectCondition != null ? auditRejectCondition : new AuditRejectCondition();
        this.auditDoneCondition = auditDoneCondition != null ? auditDoneCondition : new AuditDoneCondition();
    }

    public void init() {
        StateMachine<AuditStateEnum, AuditEventEnum, AuditContext> auditMachine = buildAuditStatemachine();
        log.info("yfcloud controller AuditStatemachine init success: OWNER_ID={}, OWNER_NAME={}, machineId={}", BaseUtil.OWNER_ID, BaseUtil.OWNER_NAME, auditMachine.getMachineId());
    }

    public void destroy() {
        log.info("yfcloud controller AuditStatemachine destroy success: OWNER_ID={}, OWNER_NAME={}", BaseUtil.OWNER_ID, BaseUtil.OWNER_NAME);
    }

    /**
     * 验证当前状态是否可以接收指定事件，返回布尔值。
     * 该方法是状态机进行状态转换前的预处理过程，可以根据业务规则和当前状态来判断事件是否合法。
     */
    public boolean doVerify(AuditStateEnum sourceState, AuditEventEnum event) {
        StateMachine<AuditStateEnum, AuditEventEnum, AuditContext> stateMachine = StateMachineFactory.get(StatemachineEnum.AUDIT_MACHINE.getCode());
        return stateMachine.verify(sourceState, event);
    }

    /**
     * 将指定事件发送给状态机，触发状态转换，并返回目标状态。
     * 该方法是状态机进行状态转换的核心逻辑，可以根据当前状态和事件来计算出目标状态，并执行相应的动作或逻辑。
     */
    public AuditStateEnum doAudit(AuditContext auditContext) {
        StateMachine<AuditStateEnum, AuditEventEnum, AuditContext> stateMachine = StateMachineFactory.get(StatemachineEnum.AUDIT_MACHINE.getCode());
        return stateMachine.fireEvent(auditContext.getSourceState(), auditContext.getEvent(), auditContext);
    }

    /**
     * 获取状态机的唯一标识符。
     * 该方法可以用于区分不同的状态机实例，方便进行管理和监控。
     */
    public String getMachineId() {
        StateMachine<AuditStateEnum, AuditEventEnum, AuditContext> stateMachine = StateMachineFactory.get(StatemachineEnum.AUDIT_MACHINE.getCode());
        return stateMachine.getMachineId();
    }

    /**
     * 使用访问者模式展示状态机的结构。
     * 该方法可以将状态机的状态、事件、转移规则等信息以图形化形式展示出来，方便理解和调试。
     */
    public void showStateMachine() {
        StateMachine<AuditStateEnum, AuditEventEnum, AuditContext> stateMachine = StateMachineFactory.get(StatemachineEnum.AUDIT_MACHINE.getCode());
        stateMachine.showStateMachine();
    }

    /**
     * 生成PlantUML格式的状态机图。
     * 该方法可以将状态机的信息以PlantUML语言的形式输出，方便进行文档记录和分享。
     */
    public String getUml() {
        StateMachine<AuditStateEnum, AuditEventEnum, AuditContext> stateMachine = StateMachineFactory.get(StatemachineEnum.AUDIT_MACHINE.getCode());
        return stateMachine.generatePlantUML();
    }

    /**
     * | From(开始状态) | To(抵达状态) | Event(事件) | When(条件)      | Perform(执行动作)  |
     * | ------------ | ---------- | ---------- | --------------- | ---------------- |
     * | 已申请        | 一级同意    | 审核通过    | passCondition    | passAction       |
     * | 一级同意      | 二级同意    | 审核通过    | passCondition    | passAction       |
     * | 二级同意      | 三级同意    | 审核通过    | passCondition    | passAction       |
     * | 三级同意      | 四级同意    | 审核通过    | passCondition    | passAction       |
     * | 四级同意      | 五级同意    | 审核通过    | passCondition    | passAction       |
     * | 已申请       | 一级不同意  | 审核驳回    | rejectCondition   | rejectAction    |
     * | 一级同意     | 二级不同意  | 审核驳回    | rejectCondition   | rejectAction    |
     * | 二级同意     | 三级不同意  | 审核驳回    | rejectCondition   | rejectAction    |
     * | 三级同意     | 四级不同意  | 审核驳回    | rejectCondition   | rejectAction    |
     * | 四级同意     | 五级不同意  | 审核驳回    | rejectCondition   | rejectAction    |
     * | 已申请      | 已完成状态  | 已完成      | doneCondition     | doneAction      |
     * | 一级同意    | 已完成状态  | 已完成      | doneCondition     | doneAction      |
     * | 二级同意    | 已完成状态  | 已完成      | doneCondition     | doneAction      |
     * | 三级同意    | 已完成状态  | 已完成      | doneCondition     | doneAction      |
     * | 四级同意    | 已完成状态  | 已完成      | doneCondition     | doneAction      |
     * | 五级同意    | 已完成状态  | 已完成      | doneCondition     | doneAction      |
     *
     * @return StateMachine stateMachine
     */
    private StateMachine<AuditStateEnum, AuditEventEnum, AuditContext> buildAuditStatemachine() {
        StateMachineBuilder<AuditStateEnum, AuditEventEnum, AuditContext> builder = StateMachineBuilderFactory.create();
        // 同意
        builder.externalTransition().from(AuditStateEnum.APPLY).to(AuditStateEnum.LEVEL_ONE_PASS)
                .on(AuditEventEnum.PASS)
                .when(auditPassCondition)
                .perform(auditPassAction);
        builder.externalTransition().from(AuditStateEnum.LEVEL_ONE_PASS).to(AuditStateEnum.LEVEL_SECOND_PASS)
                .on(AuditEventEnum.PASS)
                .when(auditPassCondition)
                .perform(auditPassAction);
        builder.externalTransition().from(AuditStateEnum.LEVEL_SECOND_PASS).to(AuditStateEnum.LEVEL_THREE_PASS)
                .on(AuditEventEnum.PASS)
                .when(auditPassCondition)
                .perform(auditPassAction);
        builder.externalTransition().from(AuditStateEnum.LEVEL_THREE_PASS).to(AuditStateEnum.LEVEL_FOUR_PASS)
                .on(AuditEventEnum.PASS)
                .when(auditPassCondition)
                .perform(auditPassAction);
        builder.externalTransition().from(AuditStateEnum.LEVEL_FOUR_PASS).to(AuditStateEnum.LEVEL_FIVE_PASS)
                .on(AuditEventEnum.PASS)
                .when(auditPassCondition)
                .perform(auditPassAction);

        // 不同意
        builder.externalTransition().from(AuditStateEnum.APPLY).to(AuditStateEnum.LEVEL_ONE_REJ)
                .on(AuditEventEnum.REJECT)
                .when(auditRejectCondition)
                .perform(auditRejectAction);
        builder.externalTransition().from(AuditStateEnum.LEVEL_ONE_PASS).to(AuditStateEnum.LEVEL_SECOND_REJ)
                .on(AuditEventEnum.REJECT)
                .when(auditRejectCondition)
                .perform(auditRejectAction);
        builder.externalTransition().from(AuditStateEnum.LEVEL_SECOND_PASS).to(AuditStateEnum.LEVEL_THREE_REJ)
                .on(AuditEventEnum.REJECT)
                .when(auditRejectCondition)
                .perform(auditRejectAction);
        builder.externalTransition().from(AuditStateEnum.LEVEL_THREE_PASS).to(AuditStateEnum.LEVEL_FOUR_REJ)
                .on(AuditEventEnum.REJECT)
                .when(auditRejectCondition)
                .perform(auditRejectAction);
        builder.externalTransition().from(AuditStateEnum.LEVEL_FOUR_PASS).to(AuditStateEnum.LEVEL_FIVE_REJ)
                .on(AuditEventEnum.REJECT)
                .when(auditRejectCondition)
                .perform(auditRejectAction);

        // 结束
        builder.externalTransitions().fromAmong(AuditStateEnum.APPLY, AuditStateEnum.LEVEL_ONE_PASS, AuditStateEnum.LEVEL_SECOND_PASS,
                        AuditStateEnum.LEVEL_THREE_PASS, AuditStateEnum.LEVEL_FOUR_PASS, AuditStateEnum.LEVEL_FIVE_PASS)
                .to(AuditStateEnum.DONE)
                .on(AuditEventEnum.DONE)
                .when(auditDoneCondition)
                .perform(auditDoneAction);
        return builder.build(StatemachineEnum.AUDIT_MACHINE.getCode());
    }
}
